//
// Created by jsjxsy on 2024/8/1.
//

//
// Created by jsjxsy on 2024/8/1.
//
#include <libavutil/log.h>
#include <stdlib.h>
#include <libavformat//avformat.h>

int main(int argc, char *argv[]) {
  char *src;
  char *dist;
  int *stream_map = NULL;
  AVFormatContext *pFmtCtx = NULL;
  AVFormatContext *oFmtCtx = NULL;
  AVPacket pkt;
  int ret = -1;
  int stream_index = 0;
//1
  av_log_set_level(AV_LOG_DEBUG);
  if (argc < 3) {
    av_log(NULL, AV_LOG_INFO, "arguments must be more than 3\n");
    exit(-1);
  }
  src = argv[1];
  dist = argv[2];
//2
  ret = avformat_open_input(&pFmtCtx, src, NULL, NULL);
  if (ret < 0) {
    av_log(NULL, AV_LOG_ERROR, "%s\n", av_err2str(ret));
    exit(-1);
  }

  avformat_alloc_output_context2(&oFmtCtx, NULL, NULL, dist);
  if (!oFmtCtx) {
    av_log(NULL, AV_LOG_ERROR, "No memory!");
    goto _ERROR;
  }
  //5
  stream_map = av_calloc(pFmtCtx->nb_streams, sizeof(int));
  if (!stream_map) {
    av_log(NULL, AV_LOG_ERROR, "No memory!");
    goto _ERROR;
  }
  for (int i = 0; i < pFmtCtx->nb_streams; i++) {
    AVStream *outStream = NULL;
    AVStream *inStream = pFmtCtx->streams[i];
    AVCodecParameters *inCodePar = inStream->codecpar;
    if (inCodePar->codec_type != AVMEDIA_TYPE_AUDIO
        && inCodePar->codec_type != AVMEDIA_TYPE_VIDEO
        && inCodePar->codec_type != AVMEDIA_TYPE_SUBTITLE) {
      stream_map[i] = -1;
      continue;
    }

    stream_map[i] = stream_index++;
    outStream = avformat_new_stream(oFmtCtx, NULL);
    if (!outStream) {
      av_log(oFmtCtx, AV_LOG_ERROR, "No memory!");
      goto _ERROR;
    }
    avcodec_parameters_copy(outStream->codecpar, inStream->codecpar);
    outStream->codecpar->codec_tag = 0;
  }

  //binding
  ret = avio_open2(&oFmtCtx->pb, dist, AVIO_FLAG_WRITE, NULL, NULL);
  if (ret < 0) {
    av_log(oFmtCtx, AV_LOG_ERROR, "%s", av_err2str(ret));
    goto _ERROR;
  }
  //7
  ret = avformat_write_header(oFmtCtx, NULL);
  if (ret < 0) {
    av_log(oFmtCtx, AV_LOG_ERROR, "%s", av_err2str(ret));
    goto _ERROR;
  }
  //8
  while (av_read_frame(pFmtCtx, &pkt) >= 0) {
    AVStream *inStream;
    AVStream *outStream;
    inStream = pFmtCtx->streams[pkt.stream_index];
    if (stream_map[pkt.stream_index] < 0) {
      av_packet_unref(&pkt);
      continue;
    }
    pkt.stream_index = stream_map[pkt.stream_index];
    outStream = oFmtCtx->streams[pkt.stream_index];
    av_packet_rescale_ts(&pkt, inStream->time_base, outStream->time_base);
    pkt.pos = -1;
    av_interleaved_write_frame(oFmtCtx, &pkt);
    av_packet_unref(&pkt);
  }
  av_write_trailer(oFmtCtx);
  _ERROR:
  if (pFmtCtx) {
    avformat_close_input(&pFmtCtx);
    pFmtCtx = NULL;
  }

  if (oFmtCtx->pb) {
    avio_close(oFmtCtx->pb);
  }

  if (oFmtCtx) {
    avformat_free_context(oFmtCtx);
    oFmtCtx = NULL;
  }
  if (stream_map) {
    av_free(stream_map);
  }
  return 0;
}